package com.yunhe.common.utils;


import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.*;
import com.yunhe.common.config.BaseConstant;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

@Slf4j
public class AliOssApiUtils {

	private static final Logger logger = LoggerFactory.getLogger(AliOssApiUtils.class);
	private static final Integer ZERO = 0;
	private static final Integer ONE_ZERO_TWO_FOUR = 1024;
	private static final Integer NINE_ZERO_ZERO = 900;
	private static final Integer THREE_TWO_SEVEN_FIVE = 3275;
	private static final Integer TWO_ZERO_FOUR_SEVEN = 2047;
	private static final Double ZERO_EIGHT_FIVE = 0.85;
	private static final Double ZERO_SIX = 0.6;
	private static final Double ZERO_FOUR_FOUR = 0.44;
	private static final Double ZERO_FOUR = 0.4;

	/***************************************** Bucket  *********************************************/
	
	/**
	 * 创建Bucket
	 * 创建的Bucket，权限是私有读写，存储类型是标准类型（Standard）
	 */
	public static void createPrivateBucket(){
		OSSClient ossClient = new OSSClient(BaseConstant.END_POINT, BaseConstant.ACCESS_KEY_ID, BaseConstant.ACCESS_KEY_SECRET);
		try {
			// 创建bucket 只能包括小写字母、数字和短横线（-）。必须以小写字母或者数字开头和结尾。长度必须在3-63字节之间
			String bucketName = "bucket-demo-test-20180126";
			ossClient.createBucket(bucketName);

		} catch (Exception e) {
			log.error("error : {}",e);;
			log.error("BucketApiManager createBucket",e.getMessage());
		} finally {
			// 关闭client
			ossClient.shutdown();
		}
	}
	
	/**
	 * 创建Bucket
	 */
	public static void createPublicBucket(){
		OSSClient ossClient = new OSSClient(BaseConstant.END_POINT, BaseConstant.ACCESS_KEY_ID, BaseConstant.ACCESS_KEY_SECRET);
		try {

			// 创建bucket 只能包括小写字母、数字和短横线（-）。必须以小写字母或者数字开头和结尾。长度必须在3-63字节之间
			String bucketName = "bucket-demo-test-20180127";
			
			CreateBucketRequest createBucketRequest= new CreateBucketRequest(bucketName);
			// 设置bucket权限为公共读，默认是私有读写
			createBucketRequest.setCannedACL(CannedAccessControlList.PublicRead);
			// 设置bucket存储类型为低频访问类型，默认是标准类型
			createBucketRequest.setStorageClass(StorageClass.IA);
			ossClient.createBucket(createBucketRequest);
			

			ossClient.shutdown();
		} catch (Exception e) {
			log.error("error : {}",e);;
			log.error("BucketApiManager createBucket",e.getMessage());
		} finally {
			// 关闭client
			log.info("==================keep to shutdown");
		}
	}
	
	/**
	 * 您可以使用OSSClient.listBuckets列举指定用户下的Bucket。
	 */
	public static void listBuckets(){
		OSSClient ossClient = new OSSClient(BaseConstant.END_POINT, BaseConstant.ACCESS_KEY_ID, BaseConstant.ACCESS_KEY_SECRET);
		try {

			// 列举bucket
			List<Bucket> buckets = ossClient.listBuckets();
			for (Bucket bucket : buckets) {
			    System.out.println(" - " + bucket.getName());
			}

		} catch (Exception e) {
			log.error("error : {}",e);;
			log.error("BucketApiManager listBuckets {}",e.getMessage());
		} finally {
			// 关闭client
			ossClient.shutdown();
		}
	}
	
	/**
	 * 删除Bucket
	 */
	public static void deleteBucket(){
		OSSClient ossClient = new OSSClient(BaseConstant.END_POINT, BaseConstant.ACCESS_KEY_ID, BaseConstant.ACCESS_KEY_SECRET);
		try {

			// 删除bucket
			ossClient.deleteBucket("bucket-demo-test-20180127");
		} catch (Exception e) {
			log.error("error : {}",e);;
			log.error("BucketApiManager deleteBucket {}",e.getMessage());
		} finally {
			// 关闭client
			ossClient.shutdown();
		}
	}
	
	/**
	 * 获取Bucket Location
	 * Bucket Location即Bucket Region，详细请参看基本概念中的Region（区域）。
	 * 您可以通过OSSClient.getBucketLocation获取bucket的权限。以下代码展示如何获取Bucket的Location：
	 */
	public static void getBucketLocation(){
		OSSClient ossClient = new OSSClient(BaseConstant.END_POINT, BaseConstant.ACCESS_KEY_ID, BaseConstant.ACCESS_KEY_SECRET);
		try {
			String location = ossClient.getBucketLocation("<bucketName>");
			log.info(location);
		} catch (Exception e) {
			log.error("error : {}",e);;
			log.error("BucketApiManager getBucketLocation {}",e.getMessage());
		} finally {
			// 关闭client
			ossClient.shutdown();
		}
	}
	
	/**
	 * 获取Bucket Info
	 * Bucket的Info包括Location、CreationDate、Owner及权限等信息。以下代码展示如何获取Bucket的Info：
	 */
	public static void getBucketInfo(){
		OSSClient ossClient = new OSSClient(BaseConstant.END_POINT, BaseConstant.ACCESS_KEY_ID, BaseConstant.ACCESS_KEY_SECRET);
		try {
			BucketInfo info = ossClient.getBucketInfo("<bucketName>");
			// Location
			info.getBucket().getLocation();
			// 创建日期
			info.getBucket().getCreationDate();
			// owner
			info.getBucket().getOwner();
			// 权限
			info.getGrants();

		} catch (Exception e) {
			log.error("error : {}",e);;
			log.error("BucketApiManager getBucketInfo {}",e.getMessage());
		} finally {
			// 关闭client
			ossClient.shutdown();
		}
	}
	
	/***************************************** 上传文件 *********************************************/
	
	/**
	 * 上传byte数组
	 * 目前我们用这个上传图片
	 */
	public static String putObjectByte(File file) {
		OSSClient ossClient = new OSSClient(BaseConstant.END_POINT, BaseConstant.ACCESS_KEY_ID, BaseConstant.ACCESS_KEY_SECRET);
		try {
			InputStream inputStream = new FileInputStream(file);
			byte[] bytes = toByteArray(inputStream);

			//获取文件后缀
			String theFileExtension = getTheFileExtension(file);
			// 创建文件路径
            String fileUrl = "yunhe/"+ dateToDateFullString(new Date()) + "/" + AppUtil.getUuid() + theFileExtension;
			// 上传
			ossClient.putObject(BaseConstant.BUCKET_NAME, fileUrl, new ByteArrayInputStream(bytes));
			return BaseConstant.FILE_HOST + fileUrl;
		} catch (Exception e) {
			log.error("上传byte数组异常");
		} finally {
			// 关闭client
			ossClient.shutdown();
		}
		return "";
	}

	public static String putObjectByte(MultipartFile file) {
		OSSClient ossClient = new OSSClient(BaseConstant.END_POINT, BaseConstant.ACCESS_KEY_ID, BaseConstant.ACCESS_KEY_SECRET);
		try {
			InputStream inputStream = file.getInputStream();
			byte[] bytes = toByteArray(inputStream);

			//获取文件后缀
			String theFileExtension = Objects.requireNonNull(file.getOriginalFilename()).substring(file.getOriginalFilename().lastIndexOf("."));
			String uuid = AppUtil.getUuid();
			// 创建文件路径
			String fileUrl = "yunhe/"+ dateToDateFullString(new Date()) + "/" + uuid + theFileExtension;
			// 上传
			ossClient.putObject(BaseConstant.BUCKET_NAME, fileUrl, new ByteArrayInputStream(bytes));

			logger.info("文件格式为: " + theFileExtension);
			//略缩图
			String imageFormat = ".bmp,.jpg,.png,.tif,.gif,.pcx,.tga,.exif,.fpx,.svg,.psd,.cdr,.pcd,.dxf,.ufo,.eps,.ai,.raw,.WMF,.webp";
			if(imageFormat.contains(theFileExtension)){
				logger.info("正在生成缩略图........");
				//压缩图片到指定120K以内,不管你的图片有多少兆,都不会超过120kb,精度还算可以,不会模糊
				byte[] abbreviationBytes = compressPicForScale(file.getBytes(), 120L);
				// 创建略缩文件路径
				String abbreviationFileUrl = "yunhe/"+ dateToDateFullString(new Date()) + "/" + uuid + "_Abbreviation" + theFileExtension;
				// 上传
				ossClient.putObject(BaseConstant.BUCKET_NAME, abbreviationFileUrl, new ByteArrayInputStream(abbreviationBytes));
			}

			return BaseConstant.FILE_HOST + fileUrl;
		} catch (Exception e) {
			log.error("上传byte数组异常");
		} finally {
			// 关闭client
			ossClient.shutdown();
		}
		return "";
	}

	/**
	 * 根据指定大小压缩图片
	 *
	 * @param imageBytes  源图片字节数组
	 * @param desFileSize 指定图片大小，单位kb
	 * @return 压缩质量后的图片字节数组
	 */
	public static byte[] compressPicForScale(byte[] imageBytes, long desFileSize) {
		if (imageBytes == null || imageBytes.length <= ZERO || imageBytes.length < desFileSize * ONE_ZERO_TWO_FOUR) {
			return imageBytes;
		}
		long srcSize = imageBytes.length;
		double accuracy = getAccuracy(srcSize / ONE_ZERO_TWO_FOUR);
		try {
			while (imageBytes.length > desFileSize * ONE_ZERO_TWO_FOUR) {
				ByteArrayInputStream inputStream = new ByteArrayInputStream(imageBytes);
				ByteArrayOutputStream outputStream = new ByteArrayOutputStream(imageBytes.length);
				Thumbnails.of(inputStream)
						.scale(accuracy)
						.outputQuality(accuracy)
						.toOutputStream(outputStream);
				imageBytes = outputStream.toByteArray();
			}
			logger.info("图片原大小={}kb | 压缩后大小={}kb",
					srcSize / ONE_ZERO_TWO_FOUR, imageBytes.length / ONE_ZERO_TWO_FOUR);
		} catch (Exception e) {
			logger.error("【图片压缩】msg=图片压缩失败!", e);
		}
		return imageBytes;
	}

	/**
	 * 自动调节精度(经验数值)
	 *
	 * @param size 源图片大小
	 * @return 图片压缩质量比
	 */
	private static double getAccuracy(long size) {
		double accuracy;
		if (size < NINE_ZERO_ZERO) {
			accuracy = ZERO_EIGHT_FIVE;
		} else if (size < TWO_ZERO_FOUR_SEVEN) {
			accuracy = ZERO_SIX;
		} else if (size < THREE_TWO_SEVEN_FIVE) {
			accuracy = ZERO_FOUR_FOUR;
		} else {
			accuracy = ZERO_FOUR;
		}
		return accuracy;
	}

	/**
	 * 删除文件
	 * 上传的文件url必须域名为https://yunheoss-hangzhou.oss-cn-hangzhou.aliyuncs.com/
	 *
	 */
	public static void deleteObject(String fileUrl) {
		OSSClient ossClient = new OSSClient(BaseConstant.END_POINT, BaseConstant.ACCESS_KEY_ID, BaseConstant.ACCESS_KEY_SECRET);

		try {
			if (StringUtils.isNotEmpty(fileUrl) && fileUrl.contains(BaseConstant.FILE_HOST)) {
				fileUrl = fileUrl.substring(55);

				// 删除文件或目录。如果要删除目录，目录必须为空。
				ossClient.deleteObject(BaseConstant.BUCKET_NAME, fileUrl);
			}
		} catch (Exception e){
			log.error("Exception e",e);
		}finally {
			// 关闭client
			ossClient.shutdown();
		}

	}

	/**
	 * 上传本地文件
	 */
	public static void putObjectLocalFile(){
		OSSClient ossClient = new OSSClient(BaseConstant.END_POINT, BaseConstant.ACCESS_KEY_ID, BaseConstant.ACCESS_KEY_SECRET);
		try {
			// 上传文件
			ossClient.putObject(BaseConstant.BUCKET_NAME, "local", new File("D://whdx.jpg"));
		} catch (Exception e) {
			log.error("error:{}",e.getMessage());
		} finally {
			// 关闭client
			ossClient.shutdown();
		}
	}

	/***************************************** 下载文件 *********************************************/

	/**
	 * OSS Java SDK提供了丰富的文件下载接口，用户可以通过以下方式从OSS中下载文件：
	 * 
	 * 流式下载 下载到本地文件 断点续传下载 范围下载
	 */

	// 流式下载: 在进行大文件下载时，往往不希望一次性处理全部内容，而是希望流式地处理，一次处理一部分内容
	public static void getStreamObject() {
		OSSClient ossClient = new OSSClient(BaseConstant.END_POINT, BaseConstant.ACCESS_KEY_ID, BaseConstant.ACCESS_KEY_SECRET);
		try {
			OSSObject ossObject = ossClient.getObject(BaseConstant.BUCKET_NAME, "q");
			// 读Object内容
			System.out.println("===============Object content:");
			BufferedReader reader = new BufferedReader(new InputStreamReader(ossObject.getObjectContent()));
			while (true) {
				String line = reader.readLine();
				if (line == null){
					break;
				}
				System.out.println("\n" + line);
			}
			reader.close();
		} catch (Exception e) {
			log.error("OssApiManager getStreamObject {}", e.getMessage());
		} finally {
			// 关闭client
			ossClient.shutdown();
		}
	}

	// 下载到本地文件: 把Object的内容下载到指定的本地文件中。如果指定的本地文件不存在则会新建。
	public static void getLocalObject() {
		OSSClient ossClient = new OSSClient(BaseConstant.END_POINT, BaseConstant.ACCESS_KEY_ID, BaseConstant.ACCESS_KEY_SECRET);
		try {
			// 下载object到文件
			ossClient.getObject(new GetObjectRequest(BaseConstant.BUCKET_NAME, "local"), new File("d://1.jpg"));
		} catch (Exception e) {
			log.error("OssApiManager getStreamObject {}", e.getMessage());
		} finally {
			// 关闭client
			ossClient.shutdown();
		}
	}

	/***************************************** 管理文件 *********************************************/
	/**
	 * 在OSS中，用户可以通过一系列的接口管理存储空间(Bucket)中的文件(Object)，
	 * 比如SetObjectAcl，GetObjectAcl，ListObjects，DeleteObject，CopyObject，DoesObjectExist等。
	 * Object的名字又称为key或object key。
	 */
	public static void doesObjectExist(){
		OSSClient ossClient = new OSSClient(BaseConstant.END_POINT, BaseConstant.ACCESS_KEY_ID, BaseConstant.ACCESS_KEY_SECRET);
		try {
			// Object是否存在
			boolean found = ossClient.doesObjectExist(BaseConstant.BUCKET_NAME, "local");
			log.info("=============doesObjectExist:{}",found);
		} catch (Exception e) {
			log.error("OssApiManager doesObjectExist {}", e.getMessage());
		} finally {
			// 关闭client
			ossClient.shutdown();
		}
	}
	
	public static void getObjectAcl(){
		OSSClient ossClient = new OSSClient(BaseConstant.END_POINT, BaseConstant.ACCESS_KEY_ID, BaseConstant.ACCESS_KEY_SECRET);
		try {
			//读取Object ACL
			ObjectAcl objectAcl = ossClient.getObjectAcl(BaseConstant.BUCKET_NAME, "local");
			log.info("==============objectAcl {}",objectAcl.getPermission().toString());
		} catch (Exception e) {
			log.error("OssApiManager getObjectAcl {}", e.getMessage());
		} finally {
			// 关闭client
			ossClient.shutdown();
		}
	}



	/**
	 * 将Date转换成formatStr格式的字符串
	 * 默认yyyyMMdd格式
	 * @param date 时间
	 * @return String
	 */
	public static String dateToDateFullString(Date date) {
		DateFormat df = new SimpleDateFormat("yyyyMMdd");
		return df.format(date);
	}

	private static byte[] toByteArray(InputStream in) throws IOException {

		ByteArrayOutputStream out = new ByteArrayOutputStream();
		byte[] buffer = new byte[1024 * 4];
		int n = 0;
		while ((n = in.read(buffer)) != -1) {
			out.write(buffer, 0, n);
		}
		return out.toByteArray();
	}

	/**
	 * 获取文件后缀名
	 */
	private static String getTheFileExtension(File file) {
		//获取文件的原始名称
		String originalFilename = file.getName();
		//获取文件的后缀名 .jpg
		String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
		return suffix;
	}

	public static void main(String[] args) throws Exception {
		File file = new File("D:/1.jpg");
//		String picPath = putObjectByte(file);
//		System.out.println(picPath);
		deleteObject("https://yunheoss-hangzhou.oss-cn-hangzhou.aliyuncs.com/yunhe/20210722/dd70ff3a1c1c49c68bf61fe5d7110ae5.jpg");
	}

	public static String putObjectByte(ByteArrayInputStream inputStream, String theFileExtension, ObjectMetadata metadata) {
		OSSClient ossClient = new OSSClient(BaseConstant.END_POINT, BaseConstant.ACCESS_KEY_ID, BaseConstant.ACCESS_KEY_SECRET);
		try {
			byte[] bytes = toByteArray(inputStream);
			//获取文件后缀
			// 创建文件路径
			String fileUrl = "yunhe/"+ dateToDateFullString(new Date()) + "/" + AppUtil.getUuid() + theFileExtension;
			// 上传
			ossClient.putObject(BaseConstant.BUCKET_NAME, fileUrl, new ByteArrayInputStream(bytes),metadata);
			return BaseConstant.FILE_HOST + fileUrl;
		} catch (Exception e) {
			log.error("上传byte数组异常");
		} finally {
			// 关闭client
			ossClient.shutdown();
		}
		return "";
	}
}
